home *** CD-ROM | disk | FTP | other *** search
/ The Utilities Experience / The Utilities Experience - Volume 1.iso / software / rendering / utilities / real3d / flox / flox.rex next >
OS/2 REXX Batch file  |  1978-06-29  |  12KB  |  384 lines

  1. /* flox.rexx - generates simulated flocking paths of a chosen number of
  2. followers tracking
  3.  * a leader path
  4.  *
  5.  *
  6.  * Program operation:
  7.  *
  8.  * Record the definition of a bspline path in Real3D and save it as an RPL
  9. script
  10.  * Run this program
  11.  * Give the name of the 'leader' bspline RPL script saved above
  12.  * Specify:
  13.  *    total number of frames
  14.  *    number of followers
  15.  *    factors affecting the behaviour of each follower
  16.  *    output file to save generated RPL bspline paths to
  17.  *
  18.  * The program then generates paths that follow the leader's in a pattern that
  19.  * attempts to simulate flocking behaviour.
  20.  *
  21.  * Revision History
  22.  *
  23.  * Date     Who     Reason
  24.  * -------------------------------------
  25.  * 25OCT94  DRO     Original
  26.  * 26OCT94  DRO     Added user-definable weighting for when the follower
  27. re-calculates
  28.  *                  its direction to the leader.
  29.  *
  30.  *
  31.  */
  32.  
  33. /* constants */
  34. TRUE=(1=1)
  35. FALSE=~TRUE
  36.  
  37. /* Set precision */
  38. NUMERIC DIGITS 6
  39.  
  40. /* Intro */
  41. SAY 'Flox v1.1'
  42. SAY '---------'
  43. SAY ''
  44. SAY 'This program takes a Real3D B-Spline defined in an RPL script file and
  45. uses it'
  46. SAY 'and a number of parameters to create RPL B-Spline definitions of paths
  47. that attempt'
  48. SAY 'to follow the leader in a flocking pattern.'
  49. SAY ''
  50. SAY 'The resulting B-Splines are written to a file, which should then be
  51. run from Real3D.'
  52. SAY ''
  53. SAY 'Note: you must use the macro recording feature of Real3D to define the
  54. leader B-Spline'
  55. SAY 'as follows:'
  56. SAY ''
  57. SAY '1.  Select Project > Macros > Record to start recording'
  58. SAY '2.  Define the B-Spline'
  59. SAY '3.  Select Project > Macros > Record to stop recording'
  60. SAY '4.  Optionally, select Project > Macros > Current to Named, and
  61. specify a filename'
  62. SAY '    under which to save the RPL.  Alternatively, the macro deafaults
  63. to T:macro.rpl'
  64. SAY ''
  65. SAY 'I based it on a bit of thinking, which may prove to be hopelessly
  66. inadequate ;-)'
  67. SAY 'David Oxley, 25 October 1994, oxleyd@logica.co.uk'
  68. SAY ''
  69.  
  70. leader_default="T:MACRO.RPL"
  71. /* Get name of leader RPL script file */
  72. SAY 'Enter leader RPL script filename, or Q to quit:
  73. [default='||leader_default||'] '
  74. PULL response
  75. IF response = "Q" THEN EXIT 10
  76. IF response = '' THEN response=leader_default
  77.  
  78. /* try to open file */
  79. IF OPEN(leaderf,response,'READ') = FALSE THEN
  80. DO
  81.   SAY 'Unable to open leader file "'||response||'".'
  82.   EXIT 10
  83. END
  84.  
  85. /* Now scan the leader file and extract the knot point x,y,z values and
  86. number of knots
  87.  *
  88.  * Sample RPL bspline macro script follows:
  89.  *
  90.  *  1 O_LOCK ( iLOCK_EXCL )
  91.  *
  92.  *  -0.95 -1.00333 0.6
  93.  *  -0.779436 -0.405667 0.6
  94.  *  -0.812256 0.226 0.6
  95.  *  -0.031538 0.721667 0.6
  96.  *  0.23841 -0.552667 0.6
  97.  *  0.697897 0.389 0.6
  98.  *  1.11 0.776667 0.6
  99.  *  7  ( count )
  100.  *  3  ( type )
  101.  *  0 ( geom. flags )
  102.  *  255 255 255 0 ( RGBA )
  103.  *  "line" ( name )
  104.  *  65664 ( flags )
  105.  *  "CEND"
  106.  *  C_LINE DROP
  107.  *
  108.  *  0 O_LOCK ( iLOCK_REMOVE )
  109.  *
  110.  *
  111.  */
  112.  
  113. /* Skip first two lines */
  114. skip=READLN(leaderf)
  115. skip=READLN(leaderf)
  116.  
  117. /* Now start reading the coords */
  118. read_complete=FALSE
  119. count=1
  120. DO UNTIL read_complete=TRUE
  121.   line=READLN(leaderf)
  122.   PARSE VAR line leader_x.count leader_y.count leader_z.count
  123.   IF DATATYPE(leader_y.count) = 'CHAR' THEN
  124.     read_complete=TRUE
  125.   ELSE
  126.     count=count+1
  127. END
  128.  
  129. /* Assumption now is that the value in leader_x.count is the count of knots */
  130. leader_knots=leader_x.count
  131.  
  132. /* We must evaluate an imaginary knot point after the last point in the
  133. leader's spline,
  134.  * by simple linear extrapolation from the last and penultimate points
  135.  * This point is necessary for calculating the aiming point for the
  136. follower as it nears
  137.  * the end of the animation, and hence the end of the leader's spline.
  138.  *
  139.  * Conveniently, count is set to one more than the number of knot points.
  140. Notice, however,
  141.  * that we don't include this last point in the leader_knots value.
  142.  */
  143. penultimate=leader_knots-1
  144. leader_x.count=leader_x.leader_knots+(leader_x.leader_knots-leader_x.penulti
  145. mate)
  146. leader_y.count=leader_y.leader_knots+(leader_y.leader_knots-leader_y.penulti
  147. mate)
  148. leader_z.count=leader_z.leader_knots+(leader_z.leader_knots-leader_z.penulti
  149. mate)
  150.  
  151. SAY 'Enter total number of frames for the animation, or Q to quit: '
  152. PULL response
  153. IF response = "Q" THEN EXIT 10
  154. total_frames=response
  155.  
  156. /* Get an outfile that doesn't already exist. */
  157. file_exists=FALSE
  158. DO UNTIL response = "Q" | (response ~= "Q" & file_exists=FALSE)
  159.   SAY 'Enter output RPL script filename, or Q to quit: '
  160.   PULL response
  161.   file_exists=EXISTS(response)
  162.   IF file_exists THEN
  163.     SAY 'File '||response||' already exists.  Pick another.'
  164. END
  165. IF response = "Q" THEN EXIT 10
  166. outfile=response
  167.  
  168. SAY 'Enter the number of followers, or Q to quit: '
  169. PULL response
  170. IF response = "Q" THEN EXIT 10
  171. num_followers=response
  172.  
  173. /* choose default values for the characteristics */
  174. independence_default=(total_frames % leader_knots)+1
  175. accuracy_default=0.5
  176. offset=(ABS(leader_x.2-leader_x.1)+ABS(leader_y.2-leader_y.1)+ABS(leader_z.2
  177. -leader_z.1))/3
  178. proximity_default=offset/3
  179. freedom_default=1
  180.  
  181. /* prop is the multiplication factor used to scale the direction vector
  182. when it is
  183.  * re-targeted to the estimated position of the leader
  184.  */
  185. prop=leader_knots/total_frames
  186.  
  187. SAY 'Enter the distance between the leader and a follower that should be
  188. considered close'
  189. SAY 'enough that the two are touching, or Q to quit:
  190. [default='||proximity_default||'] '
  191. PULL response
  192. IF response = "Q" THEN EXIT 10
  193. IF response = '' THEN response=proximity_default
  194. proximity=response
  195.  
  196. /* Now get the behaviour characteristics of each follower */
  197. DO i=1 TO num_followers
  198.   SAY 'For follower '||i||', please enter the following values:'
  199.   SAY ''
  200.   SAY 'Independence: on average, how many frames this follower will
  201. continue on its own course'
  202.   SAY 'for, before re-assessing its direction to the leader, or Q to quit:'
  203.   SAY '(1=always track leader, >1=be more independent)
  204. [default='||independence_default||'] '
  205.   PULL response
  206.   IF response = "Q" THEN EXIT 10
  207.   IF response = '' THEN response=independence_default
  208.   IF response < 1 THEN response=1
  209.   IF response > total_frames THEN response=total_frames+1
  210.   independence.i=response
  211.   SAY ''
  212.   SAY 'Accuracy: on average, how good this follower is at judging its
  213. distance from the leader,'
  214.   SAY 'or Q to quit: (0=inaccurate, 1=accurate)
  215. [default='||accuracy_default||'] '
  216.   PULL response
  217.   IF response = "Q" THEN EXIT 10
  218.   IF response = '' THEN response=accuracy_default
  219.   IF response < 0 THEN response=0
  220.   IF response > 1 THEN response=1
  221.   accuracy.i=response
  222.   SAY ''
  223.   SAY 'Freedom: a high value will, on average, cause the follower to over
  224. or undershoot the'
  225.   SAY 'position of the leader.  A low value will tend the follower to match
  226. the leader,'
  227.   SAY 'or Q to quit: (0=conform, >0=be free) [default='||freedom_default||'] '
  228.   PULL response
  229.   IF response = "Q" THEN EXIT 10
  230.   IF response = '' THEN response=freedom_default
  231.   IF response < 0 THEN response=0
  232.   IF response > 10 THEN response=10
  233.   freedom.i=response
  234.   SAY ''
  235.  
  236.   /* Now calculate where to position this follower initially.  Pretty
  237. random, but based
  238.    * on the average of the distance between the leader's first and second
  239. knot points.
  240.    * Same sort of thing to determine the initial direction
  241.    */
  242.   pos_fol_x.i=leader_x.1+(RANDU()-0.5)*offset
  243.   pos_fol_y.i=leader_y.1+(RANDU()-0.5)*offset
  244.   pos_fol_z.i=leader_z.1+(RANDU()-0.5)*offset
  245.   dir_fol_x.i=(RANDU()-0.5)*offset
  246.   dir_fol_y.i=(RANDU()-0.5)*offset
  247.   dir_fol_z.i=(RANDU()-0.5)*offset
  248. END
  249.  
  250. IF OPEN(outf,outfile,'WRITE') = FALSE THEN
  251. DO
  252.   SAY 'Unable to open output file "'||outfile||'".'
  253.   EXIT 10
  254. END
  255.  
  256. /* We have all the values we need, let's generate the followers' splines! */
  257.  
  258. DO i=1 TO num_followers
  259.   /* Keep the user informed */
  260.   SAY 'Follower '||i||', writing spline information...'
  261.  
  262.   /* RPL object locking code at the start of each new object */
  263.   CALL WRITELN(outf,'1 O_LOCK')
  264.   CALL WRITELN(outf,'')
  265.   CALL WRITELN(outf,pos_fol_x.i||' 'pos_fol_y.i||' 'pos_fol_z.i)
  266.  
  267.   curr_frame=0
  268.  
  269.   /* knot_count will be written to the script file after the coords */
  270.   knot_count=1
  271.  
  272.   /* ind_timer is based on the independence value for this follower, and
  273. determines, for
  274.    * this spline segment, how long before the follower re-assesses its position relative
  275.    * to the leader (modulo arithmetic gives the integer value)
  276.    */
  277.   ind_timer=(independence.i*judgement(accuracy.i))%1
  278.  
  279.   DO UNTIL curr_frame >= total_frames
  280.     pos_fol_x.i=pos_fol_x.i+dir_fol_x.i
  281.     pos_fol_y.i=pos_fol_y.i+dir_fol_y.i
  282.     pos_fol_z.i=pos_fol_z.i+dir_fol_z.i
  283.     ind_timer=ind_timer-1
  284.     IF (ind_timer<1) | (total_frames-curr_frame)<=1 THEN
  285.     DO
  286.       /* The timer has expired, or we are on the last frame of the
  287. animation, so we must
  288.        * write the current position to the script file.
  289.        */
  290.       CALL WRITELN(outf,pos_fol_x.i||' 'pos_fol_y.i||' 'pos_fol_z.i)
  291.       knot_count=knot_count+1
  292.       ind_timer=independence.i*judgement(accuracy.i)
  293.  
  294.       /* We must now reset the direction vector for this follower, so that
  295. it tracks,
  296.        * closely or loosely, the current position of the leader.
  297.        *
  298.        * seg_pos tells us approximately how far along the current line
  299. segment the leader is
  300.        */
  301.       seg_pos=curr_frame*leader_knots/total_frames
  302.  
  303.       /* use // (remainder) and % (modulo) to get the fractional and
  304. integer parts of seg_pos */
  305.       frac=seg_pos//1
  306.       est_knot=1+(seg_pos%1)
  307.       next_knot=est_knot+1
  308.  
  309.       /* Now work out the estimated position of the leader, where this
  310. follower will aim */
  311.       est_leader_x=leader_x.est_knot+frac*(leader_x.next_knot-leader_x.est_knot)
  312.       est_leader_y=leader_y.est_knot+frac*(leader_y.next_knot-leader_y.est_knot)
  313.       est_leader_z=leader_z.est_knot+frac*(leader_z.next_knot-leader_z.est_knot)
  314.  
  315.  
  316.       /* Calculate the new direction vector based on the estimated position
  317. of the leader
  318.        * as seen from this follower's current position.  Scale it according
  319. to the average
  320.        * number of frames per line segment, and according to the accuracy
  321. of this follower.
  322.        *
  323.        * weight is a factor added to the new direction calculation that
  324. determines how
  325.        * well the follower will eventually match the leader's position: how
  326. "free" it is.
  327.        * It is a random value based on freedom.i, centred on 0.
  328.        */
  329.       weight=(freedom.i*RANDU())-freedom.i/2
  330.  
  331. dir_fol_x.i=prop*(weight+(est_leader_x-pos_fol_x.i)-proximity)*judgement(acc
  332. uracy.i)
  333.  
  334. dir_fol_y.i=prop*(weight+(est_leader_y-pos_fol_y.i)-proximity)*judgement(acc
  335. uracy.i)
  336.  
  337. dir_fol_z.i=prop*(weight+(est_leader_z-pos_fol_z.i)-proximity)*judgement(acc
  338. uracy.i)
  339.     END /* IF ind_timer<1 */
  340.  
  341.     curr_frame=curr_frame+1
  342.   END /* DO UNTIL curr_frame >= total_frames */
  343.  
  344.   /* Now append the spline information to the script file */
  345.   CALL WRITELN(outf,knot_count||'  ( count )')
  346.   CALL WRITELN(outf,'3  ( type )')
  347.   CALL WRITELN(outf,'0 ( geom. flags )')
  348.   CALL WRITELN(outf,'255 255 255 0 ( RGBA )')
  349.   CALL WRITELN(outf,'"follower.'||i||'" ( name )')
  350.   CALL WRITELN(outf,'65664 ( flags )')
  351.   CALL WRITELN(outf,'"CEND"')
  352.   CALL WRITELN(outf,'C_LINE DROP')
  353.   CALL WRITELN(outf,'')
  354.   CALL WRITELN(outf,'0 O_LOCK ( iLOCK_REMOVE )')
  355.  
  356.   SAY 'done.'
  357.  
  358. END /* DO i=1 TO num_followers */
  359.  
  360. IF CLOSE(outf) = FALSE THEN
  361. DO
  362.   SAY 'Unable to close output file "'||outfile||'".'
  363.   EXIT 10
  364. END
  365.  
  366. SAY 'All done.  File '||outfile||' contains the generated spline(s).'
  367.  
  368. EXIT 0
  369.  
  370. /*
  371.  * Judgement is a weighted random function that returns a number in the
  372. range 0.5 to 1.5,
  373.  * the weighting being controlled by 'accuracy'.  Accuracy of 1 always
  374. returns 1;
  375.  * Accuracy of 0 returns a random value in the 0.5-1.5 range.
  376.  */
  377. judgement: PROCEDURE
  378.   ARG accuracy
  379.  
  380.   retval=(1+((1-accuracy)*(RANDU()-0.5)))
  381.   RETURN retval
  382.  
  383.  
  384.